home *** CD-ROM | disk | FTP | other *** search
- //////////
- //
- // File: AddVRActions.c
- //
- // Contains: Sample code for adding wired actions to a QuickTime VR movie.
- //
- // Written by: Tim Monroe
- // Based on existing code by Bill Wright.
- //
- // Copyright: © 1999 by Apple Computer, Inc., all rights reserved.
- //
- // Change History (most recent first):
- //
- // <2> 07/15/99 rtm added Endian macros; runs fine on both Mac and Windows;
- // added comments; removed reliance on AtomUtilities.c and
- // FileUtilities.c
- // <1> 07/14/99 rtm first file from bw; revised to sample code coding style
- //
- //
- // This file contains some sample code that adds a few wired actions to a QuickTime VR movie.
- // Currently you can add two kinds of wired actions to VR movies: (1) actions that are global
- // to a particular node and (2) actions associated with a particular hot spot in a node. An
- // example of a node-specific action might be setting the pan and tilt angles that are used
- // when the user first enters the node. An example of a hot-spot-specific action might be playing
- // a sound when the mouse is moved over the hot spot.
- //
- // All currently-supported QTVR wired actions are specific to some particular node, so the atom
- // containers implementing the actions are placed in the node information atom container that is
- // contained in the media sample for that node in the QTVR track. (See the book Virtual Reality
- // Programming With QuickTime VR 2.x for complete information on the format of VR movie files.)
- // So our job here boils down to finding a media sample in the QTVR track, constructing some atom
- // containers for our desired actions, placing those action containers into the appropriate place
- // in the media sample, and then writing the modified media sample back into the QTVR track. We
- // also need to put an atom into the media property atom container to enable wired action processing.
- //
- // For complete information about wired actions, see the chapter "Wired Sprites" in the book
- // Programming With QuickTime Sprites.
- //
- //////////
-
- #include "AddVRActions.h"
-
-
- //////////
- //
- // main/WinMain
- // The main function for this application.
- //
- //////////
-
- #if TARGET_OS_MAC
- void main (void)
- #elif TARGET_OS_WIN32
- int CALLBACK WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR theCmdLine, int nCmdShow)
- #endif
- {
- OSType myTypeList[1] = {MovieFileType};
- StandardFileReply myReply;
- OSErr myErr = noErr;
-
- #if TARGET_OS_WIN32
- InitializeQTML(0L); // initialize QuickTime Media Layer
- #endif
-
- #if TARGET_OS_MAC
- MaxApplZone(); // init everything
- InitGraf(&qd.thePort);
- InitFonts();
- FlushEvents(everyEvent, 0);
- InitWindows();
- InitMenus();
- InitDialogs(NULL);
- TEInit();
- InitCursor();
- #endif
-
- myErr = EnterMovies();
- if (myErr != noErr)
- goto bail;
-
- // elicit a movie file from the user
- StandardGetFile(NULL, 1, myTypeList, &myReply);
-
- // add some wired actions to it, if it's a QuickTime VR movie
- if (myReply.sfGood)
- AddVRAct_AddWiredActionsToQTVRMovie(&myReply.sfFile);
-
- bail:
- ExitMovies();
-
- #if TARGET_OS_WIN32
- // terminate the QuickTime Media Layer
- TerminateQTML();
- return(1);
- #endif
-
- #if TARGET_OS_MAC
- return;
- #endif
- }
-
-
- //////////
- //
- // AddVRAct_GetFirstHotSpot
- // Return, through the theHotSpotID parameter, the ID of the first hot spot in the specified atom container
- // (which is assumed to be a node information atom container).
- //
- // The returned ID is not necessarily the numerically-least ID; it's just the ID of the first hot spot atom
- // in the atom container.
- //
- //////////
-
- static OSErr AddVRAct_GetFirstHotSpot (Handle theSample, long *theHotSpotID)
- {
- QTAtom myHotSpotParentAtom = 0;
- QTAtom myHotSpotAtom = 0;
- OSErr myErr = noErr;
-
- *theHotSpotID = 0;
-
- myHotSpotParentAtom = QTFindChildByIndex(theSample, kParentAtomIsContainer, kQTVRHotSpotParentAtomType, kIndexOne, NULL);
- if (myHotSpotParentAtom != 0)
- myHotSpotAtom = QTFindChildByIndex(theSample, myHotSpotParentAtom, kQTVRHotSpotAtomType, kIndexOne, theHotSpotID);
-
- return(myErr);
- }
-
-
- //////////
- //
- // AddVRAct_CreateHotSpotActionContainer
- // Return, through the theActions parameter, an atom container that contains a hot spot action.
- //
- // Here we set the pan angle to 10.0 degrees when the hot spot is clicked.
- //
- //////////
-
- static OSErr AddVRAct_CreateHotSpotActionContainer (QTAtomContainer *theActions)
- {
-
- QTAtom myEventAtom = 0;
- QTAtom myActionAtom = 0;
- long myAction;
- float myPanAngle;
- OSErr myErr = noErr;
-
- myErr = QTNewAtomContainer(theActions);
- if (myErr != noErr)
- goto bail;
-
- myErr = QTInsertChild(*theActions, kParentAtomIsContainer, kQTEventType, kQTEventMouseClick, kIndexOne, kZeroDataLength, NULL, &myEventAtom);
- if (myErr != noErr)
- goto bail;
-
- myErr = QTInsertChild(*theActions, myEventAtom, kAction, kIndexOne, kIndexOne, kZeroDataLength, NULL, &myActionAtom);
- if (myErr != noErr)
- goto bail;
-
- myAction = EndianS32_NtoB(kActionQTVRSetPanAngle);
- myErr = QTInsertChild(*theActions, myActionAtom, kWhichAction, kIndexOne, kIndexOne, sizeof(long), &myAction, NULL);
- if (myErr != noErr)
- goto bail;
-
- myPanAngle = 10.0;
- AddVRAct_ConvertFloatToBigEndian(&myPanAngle);
- myErr = QTInsertChild(*theActions, myActionAtom, kActionParameter, kIndexOne, kIndexOne, sizeof(float), &myPanAngle, NULL);
- if (myErr != noErr)
- goto bail;
-
- bail:
- return(myErr);
- }
-
-
- //////////
- //
- // AddVRAct_CreateFrameLoadedActionContainer
- // Return, through the theActions parameter, an atom container that contains a frame-loaded event action.
- //
- // Here we set the pan angle to 180.0 degrees.
- //
- //////////
-
- static OSErr AddVRAct_CreateFrameLoadedActionContainer (QTAtomContainer *theActions)
- {
- QTAtom myEventAtom = 0;
- QTAtom myActionAtom = 0;
- long myAction;
- float myPanAngle;
- OSErr myErr = noErr;
-
- myErr = QTNewAtomContainer(theActions);
- if (myErr != noErr)
- goto bail;
-
- myErr = QTInsertChild(*theActions, kParentAtomIsContainer, kQTEventFrameLoaded, kIndexOne, kIndexOne, kZeroDataLength, NULL, &myEventAtom);
- if (myErr != noErr)
- goto bail;
-
- myErr = QTInsertChild(*theActions, myEventAtom, kAction, kIndexOne, kIndexOne, kZeroDataLength, NULL, &myActionAtom);
- if (myErr != noErr)
- goto bail;
-
- myAction = EndianS32_NtoB(kActionQTVRSetPanAngle);
- myErr = QTInsertChild(*theActions, myActionAtom, kWhichAction, kIndexOne, kIndexOne, sizeof(long), &myAction, NULL);
- if (myErr != noErr)
- goto bail;
-
- myPanAngle = 180.0;
- AddVRAct_ConvertFloatToBigEndian(&myPanAngle);
- myErr = QTInsertChild(*theActions, myActionAtom, kActionParameter, kIndexOne, kIndexOne, sizeof(float), &myPanAngle, NULL);
- if (myErr != noErr)
- goto bail;
-
- bail:
- return(myErr);
- }
-
-
- //////////
- //
- // AddVRAct_SetFrameLoadedWiredActions
- // Set the specified actions to be a frame-loaded action. If theActions is NULL, remove any existing
- // frame-loaded action from theSample.
- //
- // The theSample parameter is assumed to be a node information atom container; any actions that are global
- // to the node should be inserted at the root level of this atom container; in addition, the container type
- // should be the same as the event type and should have an atom ID of 1.
- //
- //////////
-
- static OSErr AddVRAct_SetFrameLoadedWiredActions (Handle theSample, QTAtomContainer theActions)
- {
- QTAtom myEventAtom = 0;
- QTAtom myTargetAtom = 0;
- OSErr myErr = noErr;
-
- // look for a frame-loaded action atom in the specified actions atom container
- if (theActions != NULL)
- myEventAtom = QTFindChildByID(theActions, kParentAtomIsContainer, kQTEventFrameLoaded, kIndexOne, NULL);
-
- // look for a frame-loaded action atom in the node information atom container
- myTargetAtom = QTFindChildByID(theSample, kParentAtomIsContainer, kQTEventFrameLoaded, kIndexOne, NULL);
- if (myTargetAtom != 0) {
- // if there is already a frame-loaded event atom in the node information atom container,
- // then either replace it with the one we were passed or remove it
- if (theActions != NULL)
- myErr = QTReplaceAtom(theSample, myTargetAtom, theActions, myEventAtom);
- else
- myErr = QTRemoveAtom(theSample, myTargetAtom);
- } else {
- // there is no frame-loaded event atom in the node information atom container,
- // so add in the one we were passed
- if (theActions != NULL)
- myErr = QTInsertChildren(theSample, kParentAtomIsContainer, theActions);
- }
-
- return(myErr);
- }
-
-
- //////////
- //
- // AddVRAct_SetWiredActionsToHotSpot
- // Set the specified actions to be a hot-spot action. If theActions is NULL, remove any existing
- // hot-spot actions for the specified hot spot from theSample.
- //
- //////////
-
- static OSErr AddVRAct_SetWiredActionsToHotSpot (Handle theSample, long theHotSpotID, QTAtomContainer theActions)
- {
- QTAtom myHotSpotParentAtom = 0;
- QTAtom myHotSpotAtom = 0;
- short myCount,
- myIndex;
- OSErr myErr = paramErr;
-
- myHotSpotParentAtom = QTFindChildByIndex(theSample, kParentAtomIsContainer, kQTVRHotSpotParentAtomType, kIndexOne, NULL);
- if (myHotSpotParentAtom == NULL)
- goto bail;
-
- myHotSpotAtom = QTFindChildByID(theSample, myHotSpotParentAtom, kQTVRHotSpotAtomType, theHotSpotID, NULL);
- if (myHotSpotAtom == NULL)
- goto bail;
-
- // see how many events are already associated with the specified hot spot
- myCount = QTCountChildrenOfType(theSample, myHotSpotAtom, kQTEventType);
-
- for (myIndex = myCount; myIndex > 0; myIndex--) {
- QTAtom myTargetAtom = 0;
-
- // remove all the existing events
- myTargetAtom = QTFindChildByIndex(theSample, myHotSpotAtom, kQTEventType, myIndex, NULL);
- if (myTargetAtom != 0) {
- myErr = QTRemoveAtom(theSample, myTargetAtom);
- if (myErr != noErr)
- goto bail;
- }
- }
-
- if (theActions) {
- myErr = QTInsertChildren(theSample, myHotSpotAtom, theActions);
- if (myErr != noErr)
- goto bail;
- }
-
- bail:
- return(myErr);
- }
-
-
- //////////
- //
- // AddVRAct_WriteMediaPropertyAtom
- // Add a media property action to the specified media.
- //
- // We assume that the data passed to us through the theProperty parameter is big-endian.
- //
- //////////
-
- static OSErr AddVRAct_WriteMediaPropertyAtom (Media theMedia, long thePropertyID, long thePropertySize, void *theProperty)
- {
- QTAtomContainer myPropertyAtom = NULL;
- QTAtom myAtom = 0;
- OSErr myErr = noErr;
-
- // get the current media property atom
- myErr = GetMediaPropertyAtom(theMedia, &myPropertyAtom);
- if (myErr != noErr)
- goto bail;
-
- // if there isn't one yet, then create one
- if (myPropertyAtom == NULL) {
- myErr = QTNewAtomContainer(&myPropertyAtom);
- if (myErr != noErr)
- goto bail;
- }
-
- // see if there is an existing atom of the specified type; if not, then create one
- myAtom = QTFindChildByID(myPropertyAtom, kParentAtomIsContainer, thePropertyID, kIndexOne, NULL);
- if (myAtom == NULL) {
- myErr = QTInsertChild(myPropertyAtom, kParentAtomIsContainer, thePropertyID, kIndexOne, kIndexZero, kZeroDataLength, NULL, &myAtom);
- if ((myErr != noErr) || (myAtom == NULL))
- goto bail;
- }
-
- // set the data of the specified atom to the data passed in
- myErr = QTSetAtomData(myPropertyAtom, myAtom, thePropertySize, (Ptr)theProperty);
- if (myErr != noErr)
- goto bail;
-
- // write the new atom data out to the media property atom
- myErr = SetMediaPropertyAtom(theMedia, myPropertyAtom);
-
- bail:
- if (myPropertyAtom != NULL)
- myErr = QTDisposeAtomContainer(myPropertyAtom);
-
- return(myErr);
- }
-
-
- //////////
- //
- // AddVRAct_AddWiredActionsToQTVRMovie
- // Add some wired actions to the specified QTVR movie.
- //
- // Wired actions are added to a QTVR movie by adding atom containers in the appropriate locations.
- //
- //////////
-
- static void AddVRAct_AddWiredActionsToQTVRMovie (FSSpec *theFSSpec)
- {
- short myResID = 0;
- short myResRefNum = -1;
- Movie myMovie = NULL;
- Track myTrack = NULL;
- Media myMedia = NULL;
- TimeValue myTrackOffset;
- TimeValue myMediaTime;
- TimeValue mySampleDuration;
- TimeValue mySelectionDuration;
- TimeValue myNewMediaTime;
- QTVRSampleDescriptionHandle myQTVRDesc = NULL;
- Handle mySample = NULL;
- short mySampleFlags;
- Fixed myTrackEditRate;
- QTAtomContainer myActions = NULL;
- Boolean myHasActions;
- long myHotSpotID = 0L;
- OSErr myErr = noErr;
-
- //////////
- //
- // open the movie file and get the QTVR track from the movie
- //
- //////////
-
- // open the movie file for reading and writing
- myErr = OpenMovieFile(theFSSpec, &myResRefNum, fsRdWrPerm);
- if (myErr != noErr)
- goto bail;
-
- myErr = NewMovieFromFile(&myMovie, myResRefNum, &myResID, NULL, newMovieActive, NULL);
- if (myErr != noErr)
- goto bail;
-
- // find the first QTVR track in the movie;
- // this assumes that the movie is a QuickTime VR movie formatted according to version 2.0
- // or later (version 1.0 VR movies don't have a QTVR track)
- myTrack = GetMovieIndTrackType(myMovie, kIndexOne, kQTVRQTVRType, movieTrackMediaType);
- if (myTrack == NULL)
- goto bail;
-
- //////////
- //
- // get first media sample in the QTVR track
- //
- // the QTVR track contains one media sample for each node in the movie;
- // that sample contains a node information atom container, which contains general information
- // about the node (such as its type, its ID, its name, and a list of its hot spots)
- //
- //////////
-
- myMedia = GetTrackMedia(myTrack);
- if (myMedia == NULL)
- goto bail;
-
- myTrackOffset = GetTrackOffset(myTrack);
- myMediaTime = TrackTimeToMediaTime(myTrackOffset, myTrack);
-
- // allocate some storage to hold the sample description for the QTVR track
- myQTVRDesc = (QTVRSampleDescriptionHandle)NewHandle(4);
- if (myQTVRDesc == NULL)
- goto bail;
-
- mySample = NewHandle(0);
- if (mySample == NULL)
- goto bail;
-
- myErr = GetMediaSample(myMedia, mySample, 0, NULL, myMediaTime, NULL, &mySampleDuration, (SampleDescriptionHandle)myQTVRDesc, NULL, 1, NULL, &mySampleFlags);
- if (myErr != noErr)
- goto bail;
-
- //////////
- //
- // add frame-loaded actions
- //
- //////////
-
- // create an action container for frame-loaded actions
- myErr = AddVRAct_CreateFrameLoadedActionContainer(&myActions);
- if (myErr != noErr)
- goto bail;
-
- // add frame-loaded actions to sample
- myErr = AddVRAct_SetFrameLoadedWiredActions(mySample, myActions);
- if (myErr != noErr)
- goto bail;
-
- myErr = QTDisposeAtomContainer(myActions);
- if (myErr != noErr)
- goto bail;
-
- //////////
- //
- // add hot-spot actions
- //
- //////////
-
- // find the first hot spot in the selected node
- myErr = AddVRAct_GetFirstHotSpot(mySample, &myHotSpotID);
- if ((myErr != noErr) || (myHotSpotID == 0))
- goto bail;
-
- // create an action container for hot-spot actions
- myErr = AddVRAct_CreateHotSpotActionContainer(&myActions);
- if (myErr != noErr)
- goto bail;
-
- // add hot-spot actions to sample
- myErr = AddVRAct_SetWiredActionsToHotSpot(mySample, myHotSpotID, myActions);
- if (myErr != noErr)
- goto bail;
-
- //////////
- //
- // replace sample in media
- //
- //////////
-
- myTrackEditRate = GetTrackEditRate(myTrack, myTrackOffset);
- if (GetMoviesError() != noErr)
- goto bail;
-
- GetTrackNextInterestingTime(myTrack, nextTimeMediaSample | nextTimeEdgeOK, myTrackOffset, fixed1, NULL, &mySelectionDuration);
- if (GetMoviesError() != noErr)
- goto bail;
-
- myErr = DeleteTrackSegment(myTrack, myTrackOffset, mySelectionDuration);
- if (myErr != noErr)
- goto bail;
-
- myErr = BeginMediaEdits(myMedia);
- if (myErr != noErr)
- goto bail;
-
- myErr = AddMediaSample( myMedia,
- mySample,
- 0,
- GetHandleSize(mySample),
- mySampleDuration,
- (SampleDescriptionHandle)myQTVRDesc,
- 1,
- mySampleFlags,
- &myNewMediaTime);
- if (myErr != noErr)
- goto bail;
-
- myErr = EndMediaEdits(myMedia);
- if (myErr != noErr)
- goto bail;
-
- // add the media to the track
- myErr = InsertMediaIntoTrack(myTrack, myTrackOffset, myNewMediaTime, mySelectionDuration, myTrackEditRate);
- if (myErr != noErr)
- goto bail;
-
- //////////
- //
- // set the actions property atom, to enable wired action processing
- //
- //////////
-
- myHasActions = true; // since sizeof(Boolean) == 1, there is no need to swap bytes here
- myErr = AddVRAct_WriteMediaPropertyAtom(myMedia, kSpriteTrackPropertyHasActions, sizeof(Boolean), &myHasActions);
- if (myErr != noErr)
- goto bail;
-
- //////////
- //
- // update the movie resource
- //
- //////////
-
- myErr = UpdateMovieResource(myMovie, myResRefNum, myResID, NULL);
- if (myErr != noErr)
- goto bail;
-
- // close the movie file
- myErr = CloseMovieFile(myResRefNum);
-
- bail:
- if (myActions != NULL)
- (void)QTDisposeAtomContainer(myActions);
-
- if (mySample != NULL)
- DisposeHandle(mySample);
-
- if (myQTVRDesc != NULL)
- DisposeHandle((Handle)myQTVRDesc);
-
- if (myMovie != NULL)
- DisposeMovie(myMovie);
- }
-
-
- //////////
- //
- // AddVRAct_ConvertFloatToBigEndian
- // Convert the specified floating-point number to big-endian format.
- //
- //////////
-
- void AddVRAct_ConvertFloatToBigEndian (float *theFloat)
- {
- unsigned long *myLongPtr;
-
- myLongPtr = (unsigned long *)theFloat;
- *myLongPtr = EndianU32_NtoB(*myLongPtr);
- }
-
-